<!DOCTYPE html>
<html>
<head>
    <style>
        body { 
            margin: 0;
            padding: 10px;
            background: #1e1e1e;
            color: #d4d4d4;
            font-family: monospace;
        }
        #output {
            white-space: pre-wrap;
            word-wrap: break-word;
            font-size: 14px;
            line-height: 1.4;
        }
        .ansi-black-fg { color: #000000; }
        .ansi-red-fg { color: #cd0000; }
        .ansi-green-fg { color: #00cd00; }
        .ansi-yellow-fg { color: #cdcd00; }
        .ansi-blue-fg { color: #0000ee; }
        .ansi-magenta-fg { color: #cd00cd; }
        .ansi-cyan-fg { color: #00cdcd; }
        .ansi-white-fg { color: #e5e5e5; }
        .ansi-bright-black-fg { color: #7f7f7f; }
        .ansi-bright-red-fg { color: #ff0000; }
        .ansi-bright-green-fg { color: #00ff00; }
        .ansi-bright-yellow-fg { color: #ffff00; }
        .ansi-bright-blue-fg { color: #5c5cff; }
        .ansi-bright-magenta-fg { color: #ff00ff; }
        .ansi-bright-cyan-fg { color: #00ffff; }
        .ansi-bright-white-fg { color: #ffffff; }
        .ansi-bold { font-weight: bold; }
        .ansi-dim { opacity: 0.7; }
        .ansi-italic { font-style: italic; }
        .ansi-underline { text-decoration: underline; }
    </style>
</head>
<body>
    <pre id="output"></pre>
    <script>
        const output = document.getElementById('output');
        
        // ANSI escape code parser
        class AnsiParser {
            constructor() {
                this.fg = null;
                this.bg = null;
                this.bold = false;
                this.dim = false;
                this.italic = false;
                this.underline = false;
            }

            reset() {
                this.fg = null;
                this.bg = null;
                this.bold = false;
                this.dim = false;
                this.italic = false;
                this.underline = false;
            }

            getStyle() {
                const classes = [];
                if (this.fg) classes.push(`ansi-${this.fg}-fg`);
                if (this.bg) classes.push(`ansi-${this.bg}-bg`);
                if (this.bold) classes.push('ansi-bold');
                if (this.dim) classes.push('ansi-dim');
                if (this.italic) classes.push('ansi-italic');
                if (this.underline) classes.push('ansi-underline');
                return classes.join(' ');
            }

            parse(text) {
                const result = [];
                const regex = /\x1b\[(\d+)m|([^\x1b]+)/g;
                let match;
                
                while ((match = regex.exec(text)) !== null) {
                    if (match[1]) {
                        // ANSI escape code
                        const code = parseInt(match[1]);
                        switch(code) {
                            case 0: this.reset(); break;
                            case 1: this.bold = true; break;
                            case 2: this.dim = true; break;
                            case 3: this.italic = true; break;
                            case 4: this.underline = true; break;
                            case 30: this.fg = 'black'; break;
                            case 31: this.fg = 'red'; break;
                            case 32: this.fg = 'green'; break;
                            case 33: this.fg = 'yellow'; break;
                            case 34: this.fg = 'blue'; break;
                            case 35: this.fg = 'magenta'; break;
                            case 36: this.fg = 'cyan'; break;
                            case 37: this.fg = 'white'; break;
                            case 90: this.fg = 'bright-black'; break;
                            case 91: this.fg = 'bright-red'; break;
                            case 92: this.fg = 'bright-green'; break;
                            case 93: this.fg = 'bright-yellow'; break;
                            case 94: this.fg = 'bright-blue'; break;
                            case 95: this.fg = 'bright-magenta'; break;
                            case 96: this.fg = 'bright-cyan'; break;
                            case 97: this.fg = 'bright-white'; break;
                        }
                    } else if (match[2]) {
                        // Regular text
                        const style = this.getStyle();
                        result.push(style 
                            ? `<span class="${style}">${match[2]}</span>`
                            : match[2]);
                    }
                }
                return result.join('');
            }
        }

        const parser = new AnsiParser();
        const decoder = new TextDecoder();
        
       function displayError(message) {
            const errorText = `\n\x1b[1;31mError: ${message}\x1b[0m\n`;  // Red, bold error message
            output.innerHTML += parser.parse(errorText);
            window.scrollTo(0, document.body.scrollHeight);
        }

        fetch('{stream_url}')
            .then(response => {
                if (!response.ok) {
                    // For HTTP errors, read the error message from response
                    return response.text().then(text => {
                        try {
                            // Try to parse as JSON (FastAPI error format)
                            const error = JSON.parse(text);
                            throw new Error(error.detail || error.message || text);
                        } catch (e) {
                            // If not JSON, use the raw text
                            throw new Error(text);
                        }
                    });
                }
                const reader = response.body.getReader();

                function readStream() {
                    reader.read().then(({ done, value }) => {
                        if (done) {
                            return;
                        }
                        try {
                            const text = decoder.decode(value, { stream: true });
                            output.innerHTML += parser.parse(text);
                            window.scrollTo(0, document.body.scrollHeight);
                            readStream();
                        } catch (err) {
                            displayError(`Failed to process stream: ${err.message}`);
                            throw err;
                        }
                    }).catch(err => {
                        displayError(`Failed to read stream: ${err.message}`);
                        console.error('Stream read error:', err);
                    });
                }

                readStream();
            })
            .catch(err => {
                displayError(err.message);
                console.error('Fetch error:', err);
            });
    </script>
</body>
</html>
